#include <System.h>
#include <boost/asio.hpp>
#include <iostream>
#include <memory>
#include <packet/Packet.h>
#include <packet/PacketStream.h>
#include <string>

using namespace sys;

using tcp = boost::asio::ip::tcp;
using namespace boost;

class chat_session
    : public std::enable_shared_from_this<chat_session> {
private:
    tcp::socket          socket;
    std::vector<char>    read_msg_;
    packet::PacketStream stream;

public:
    chat_session(tcp::socket _socket)
        : socket(std::move(_socket)),
          stream(socket) {
        read_msg_.resize(1024);
    }
    ~chat_session() {
        System::log << " ~chat_session" << std::endl;
    }

    void start() {
        // do_read();
        // do_read_headler();
        do_read_stream();
    };

    void do_read_stream() {
        auto self(shared_from_this());
        stream.async_read([this, self](packet::Packet::SharedPtr pack, packet::error_code ec) {
            if (!ec) {
                System::log << "head: { type:" << pack->type() << ", body_len:" << pack->bodyLenght() << "}" << std::endl;
                System::log << "body:" << std::string(pack->body(), pack->bodyLenght()) << std::endl;
                do_read_stream();
            }
            else {
                System::err << ec.what() << std::endl;
            }
        });
    }

    void do_read_headler() {
        auto self(shared_from_this());

        auto pack = std::make_shared<packet::Packet>();

        boost::asio::async_read(socket,
            boost::asio::buffer(pack->data(), pack->headLenght()),

            [this, self, pack](boost::system::error_code ec, std::size_t len /*length*/) {
                if (!ec) {
                    pack->decode();
                    System::log << "head: { len:" << len << ", type:" << pack->type() << ", body_len:" << pack->bodyLenght() << "}" << std::endl;

                    do_read_body(pack);
                }
                else {
                    System::err << ec.what() << std::endl;
                }
            });
    }

    void do_read_body(packet::Packet::SharedPtr pack) {
        auto self(shared_from_this());

        boost::asio::async_read(socket,
            boost::asio::buffer(pack->body(), pack->bodyLenght()),

            [this, self, pack](boost::system::error_code ec, std::size_t len /*length*/) {
                if (!ec) {
                    pack->decode();
                    System::log << "body:" << len << ":" << std::string(pack->body(), pack->bodyLenght()) << std::endl;

                    do_read_headler();
                }
                else {
                    System::err << ec.what() << std::endl;
                }
            });
    }

    void do_read() {
        auto self(shared_from_this());

        memset(read_msg_.data(), 0, read_msg_.size());
        boost::asio::async_read(socket,
            boost::asio::buffer(read_msg_.data(), read_msg_.size()),

            [this, self](boost::system::error_code ec, std::size_t len /*length*/) {
                if (!ec) {
                    System::log << "read:" << len << ":" << std::string(self->read_msg_.data(), self->read_msg_.size()) << std::endl;
                    self->do_read();
                }
                else {
                    System::err << ec.what() << std::endl;
                }
            });
    }
};

class test_tcp_server {
private:
    asio::io_context& io_context;
    tcp::acceptor     acceptor;

public:
    test_tcp_server(asio::io_context& io_context, tcp::endpoint& endpoint)
        : io_context(io_context),
          acceptor(io_context, endpoint) { do_accept(); }

    void do_accept() {
        acceptor.async_accept(
            [this](boost::system::error_code ec, tcp::socket socket) {
                if (!ec) {
                    System::out << "new connection" << std::endl;
                    std::make_shared<chat_session>(std::move(socket))->start();
                }

                do_accept();
            });
    }
    ~test_tcp_server() {}
};

int main(int argc, char const* argv[]) {
    asio::io_context io_context;
    tcp::endpoint    endpoint(tcp::v4(), 4090);

    test_tcp_server server(io_context, endpoint);
    io_context.run();
    return 0;
}
